﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Management.Instrumentation;
using System.Security.Cryptography;
using Baik.Interface.Execution;
using Baik.Interface.Framework;

namespace Baik.Execution
{
    public class Scope : NonRecursingScope
    {
        public IExceptionThrower Thrower { get; private set; }
        public Scope(IInjector inject) : base(inject)
        {
            Thrower = inject.Instantiate<IExceptionThrower>();
            Dictionary.Add("getMember",  BuiltInFactory.Function(Inject, GetMember));
            Dictionary.Add("getMembers",  BuiltInFactory.Function(Inject, GetAllMembers));
            Dictionary.Add("hasLocalMember",  BuiltInFactory.Function(Inject, HasLocalMember));
            Dictionary.Add("getLocalMembers",  BuiltInFactory.Function(Inject, GetMembers));
        
        }
        private IObject HasLocalMember(IEnumerable<IObject> arg)
        {
            var enumerable = arg as IObject[] ?? arg.ToArray();
            if (enumerable.Length < 1) Thrower.Throw("Must Supply a single valid string when trying to get a member via its name");
            var obj = enumerable[0];
            if (!(obj is IString)) Thrower.Throw("Must Supply a single valid string when trying to get a member via its name");
            var str = obj as IString;
            var value = str.Value;
            return (getkeynames().Contains(value)) ? (IBoolean)Inject.Instantiate<ITrue>() : (IBoolean)Inject.Instantiate<IFalse>();
        }

        private IObject GetMember(IEnumerable<IObject> arg)
        {
            var enumerable = arg as IObject[] ?? arg.ToArray();
            if (enumerable.Length < 1) Thrower.Throw("Must Supply a single valid string when trying to get a member via its name");
            var obj = enumerable[0];
            if (!(obj is IString)) Thrower.Throw("Must Supply a single valid string when trying to get a member via its name");
            var str = obj as IString;
            var value = str.Value;
            return this.Get(value);
        }

        

        public IObject GetMembers(IEnumerable<IObject> arg)
        {
            var rtn = Inject.Instantiate<IArray>();
            rtn.Items = getkeynames().Select(CreateString);
            return rtn;
        }
        

        
    }
    public class NonRecursingScope : IScope
    {
        public IEnumerable<IScope> Parents  
        {
            get { return _parents; }
            set { _parents = new List<IScope>(value); }
        }
        protected IString CreateString(string s)
        {
            var rtn = Inject.Instantiate<IString>();
            rtn.Value = s;
            return rtn;
        }
      
      
        public IInjector Inject { get; private set; }
        public IScope DeclarationScope { get; set; }

        public Dictionary<string, IObject> Dictionary { get; set; }
        protected List<IScope> _parents;
        private Dictionary<string, IObject> _dictionary;

        public void Inherit(IObject parent)
        {
            _parents.Add(parent);
        }
        public NonRecursingScope(IInjector inject)
        {
            _parents = new List<IScope>();
            Inject = inject;
            Dictionary = new Dictionary<string, IObject>();    
        }


        protected List<string> getkeynames()
        {
            var lst = new List<string>();
            foreach (var kvp in Dictionary)
            {
                lst.Add(kvp.Key);
            }
            return lst;
        }
        
        public IObject Get(string name)
        {
            IObject rtn = TryGet(name, out rtn) ? rtn : Inject.Instantiate<IUndefined>();;
            if (rtn is IUndefined && DeclarationScope != null) rtn = DeclarationScope.Get(name);
            if (rtn is IUndefined) rtn = GetInherited(name, rtn);
            return rtn;
        }

        public IObject GetAllMembers(IEnumerable<IObject> arg)
        {
            return GetAllMembers();
        }

        public IArray GetAllMembers()
        {
            var lst = new List<IObject>(getkeynames().Select(CreateString));
            _parents.ForEach(p => lst.AddRange(p.GetAllMembers().Items));
            var rtn = Inject.Instantiate<IArray>();
            rtn.Items = lst;
            return rtn;
        }


        private IObject GetInherited(string name, IObject rtn)
        {
            foreach (var p in Parents)
                {
                    rtn = p.Get(name);
                    if (!(rtn is IUndefined)) return rtn;
                }
            return rtn;
        }
        public bool TryGet(string name, out IObject val)
        {
            return Dictionary.TryGetValue(name, out val);
        }
        public void Assign(string name, IObject value)
        {
            IObject oldvalue = null;
            if (TryGet(name, out oldvalue))
            {
                oldvalue.DeAssign();
                Dictionary.Remove(name);
            }
            if (value is IFunction)
            {
                ((IFunction) value).HostingScope = this;
            }
            if (string.IsNullOrEmpty(value.Name))
            {
                value.Name = name;
            }
            Dictionary.Add(name, value);
            value.Assign();
        }
        public virtual string AsString()
        {
            return "Scope";
        }
    }
}
